package com.fsp.ConvertUtil;

import com.google.common.collect.Maps;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * @author fushengping
 * @className ConvertUtil
 * @description List 与 Map 之间的转化和过滤转化
 * @date 2021/12/1 17:38
 */
@Component
public class ConvertUtil {
    public ConvertUtil() {
    }

    /**
     * 将List转为Map
     * @param list         原数据
     * @param keyExtractor Key的抽取规则
     * @param <K>          Key
     * @param <V>          Value
     *           Function<V, K>是函数式接口，内置的apply函数可以将V类型数据，用apply(K)以K类型返回
     *           而Person::getName为方法引用，即对apply方法进行了重写
     * @return
     */
    public static <K, V> Map<K, V> listToMap(List<V> list, Function<V, K> keyExtractor) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        Map<K, V> map = new HashMap<>(list.size());//转化后的类型用它来接收
        for (V element : list) {
            //通过重写apply方法进行类型转换
            K key = keyExtractor.apply(element);
            if (key == null) {
                continue;
            }
            map.put(key, element);
        }
        return map;
    }

    /**
     * 将List转为Map，可以指定过滤规则
     *
     * @param list         原数据
     * @param keyExtractor Key的抽取规则
     * @param predicate    过滤规则 (断定型接口)
     * @param <K>          Key
     * @param <V>          Value
     * @return
     */
    public static <K,V> Map<K,V> listToMapFilter(List<V> list, Function<V,K> keyExtractor, Predicate<V> predicate) {
        if (list == null || list.isEmpty()) {
            return new HashMap<>();
        }
        //转化后的类型用它来接收
        Map<K,V> map = new HashMap<>(list.size());
        for (V element : list) {
            //重写Function函数式接口的apply方法进行类型转换
            K key = keyExtractor.apply(element);
            //重写Predicate判断型函数式接口的方法进行过滤
            if (key == null || !predicate.test((V) element)) { //过滤之后再进行转化
                continue;
            }
            //原本list中的内容element需要转存在map(key,value)的value中
            map.put(key,element);
        }
        return map;
    }

    /**
     * 将List映射为List，比如List<Person> personList转为List<String> nameList
     *
     * @param originList 原数据
     * @param mapper     映射规则
     * @param <T>        原数据的元素类型
     * @param <R>        新数据的元素类型
     * @return
     */
    public static <T, R> List<R> resultToList(List<T> originList, Function<T,R> mapper) {
        if (originList == null || originList.isEmpty()) {
            return new ArrayList<>();
        }
        //进行逻辑转化
        List<R> newList = new ArrayList<>(originList.size()); //转化后的类型用它来接收
        for (T originElement : originList) {
            R newElement = mapper.apply(originElement);
            if (newElement == null) {
                continue;
            }
            newList.add(newElement);
        }
        return newList;
    }

    /**
     * 将List映射为List，比如List<Person> personList转为List<String> nameList
     * 可以指定过滤规则
     *
     * @param originList 原数据
     * @param mapper     映射规则
     * @param predicate  过滤规则(判断式接口函数:过滤之后再进行转化)
     * @param <T>        原数据的元素类型
     * @param <R>        新数据的元素类型
     * @return
     */
    public static  <T,R> List<R> resultToListFilter(List<T> originList, Function<T,R> mapper, Predicate<T> predicate) {
        if (originList == null || originList.isEmpty()) {
            return new ArrayList<>();
        }
        //接收类型转化后的数组
        List<R> newList = new ArrayList<>(originList.size());
        //进行类型转化和过滤
        for (T originElement : originList) {
            //使用接口函数重写apply方法
            R newElement = mapper.apply(originElement);
            //判断该接口函数转化的值是否为空，进行过滤
            if (newElement == null || predicate.test(originElement)){
                continue;
            }
            newList.add(newElement);
        }
        return newList;
    }

    /**
     * groupBy分组
     *
     * @param originList   需要分组的List
     * @param keyExtractor 分组规则（key）
     * @param <T>
     * @param <K>
     * @return
     */
    public static <T, K> Map<K, List<T>> groupBy(List<T> originList, Function<T, K> keyExtractor) {
        if (originList == null || originList.isEmpty()) {
            //使用guava包和原生HashMap一样，只是简化了，不用手动写泛型
            return Maps.newHashMap();
        }

        Map<K, List<T>> map = new HashMap<>();
        for (T element : originList) {
            K key = keyExtractor.apply(element);
            if (key == null) {
                continue;
            }

            List<T> vList = map.get(key);
            if (vList == null || vList.isEmpty()) {
                // 如果分组list为空，那么构造list存入
                vList = new ArrayList<>();
                vList.add(element);
                map.put(key, vList);
            } else {
                // 否则取出分组list并存入本次element
                map.get(key).add(element);
            }
        }

        return map;
    }

    /*测试用例*/

    private static List<Person> list;

    static {
        list = new ArrayList<>();
        list.add(new Person("i", 18, "杭州", 999.9));
        list.add(new Person("am", 19, "温州", 777.7));
        list.add(new Person("iron", 21, "杭州", 888.8));
        list.add(new Person("man", 17, "宁波", 888.8));
    }

    public Map<String,Person> getMap() {
        Map<String, Person> stringPersonMap = listToMap(list, Person::getName);
        return stringPersonMap;
    }

    public static void main(String[] args) {
        System.out.println(list);
        System.out.println("\n--------------------------------------\n");
        /*将List转为Map,方法引用重写(这里方法引用：取每个map内name值，作为map的key)*/
        Map<String, Person> stringPersonMap = listToMap(list, Person::getName);
        //实际上就是对函数式接口的apply方法进行重写(lambda对apply进行重写)
        Map<String, Person> stringPersonMap1 = listToMap(list, person -> person.getName());
        System.out.println(stringPersonMap);
        System.out.println(stringPersonMap1);
        System.out.println("\n--------------------------------------\n");

        /*将List转为Map,方法引用重写，并设置过滤规则*/
        Map<String, Person> stringPersonMap2 = listToMapFilter(list, Person::getName, person -> person.getAge() > 17);
        System.out.println(stringPersonMap2);
        System.out.println("\n--------------------------------------\n");

        /*将List映射为List，比如List<Person> personList转为List<String> nameList*/
        List<String> strings1 = resultToList(list, Person::getName);
        List<String> strings2 = resultToList(list, person -> person.getName());
        System.out.println(strings1);
        System.out.println(strings2);
        System.out.println("\n--------------------------------------\n");

        /*将List映射为List，并进行过滤，比如List<Person> personList转为List<String> nameList*/
        List<String> stringList = resultToListFilter(list, Person::getName, person -> person.getAge() > 18);
        System.out.println(stringList);
        System.out.println("\n--------------------------------------\n");

        /*模仿group by 进行分组*/
        Map<String, List<Person>> stringListMap = groupBy(list, Person::getAddress);// 相同地址为一个List
        System.out.println(stringListMap);
    }
}
